#include "TriMesh.h"
#include "BasicGeometry.h"
#include "Renderer.h"
#include "RenderQueue.h"

namespace Nezha
{

	//class BuiltInMeshCreator : public SceneObjectFactory
	//{
	//public:
	//	class Param : public SceneObjectCreation
	//	{
	//	public:
	//		BuiltInMesh mMeshType;
	//		u16 mXExtent;
	//		u16 mYExtent;

	//		Param()
	//			:SceneObjectCreation(SOT_GEOMETRY),
	//			mMeshType(BIM_PLANE),
	//			mXExtent(1),
	//			mYExtent(1)
	//		{
	//		}
	//	};

	//	BuiltInMeshCreator()
	//	{
	//		mSceneObjectType = SOT_GEOMETRY;
	//	}

	//protected:
	//	SceneNode* createImpl(const _string& name, SceneObjectCreation* params)
	//	{
	//		Param* p = static_cast<Param*>(params);

	//		TriangleListMesh* tlm = NULL;

	//		switch(p->mMeshType)
	//		{
	//		case BIM_BOX:
	//			{
	//				tlm = TriangleListMesh::CreateBox(1.0f, 1.0f, 1.0f);
	//			}
	//			break;
	//		case BIM_PLANE:
	//			{
	//				tlm = TriangleListMesh::CreatePlane(p->mXExtent, p->mYExtent);
	//			}
	//			break;
	//		case BIM_SPHERE:
	//			{
	//				tlm = TriangleListMesh::CreateSphere(p->mXExtent, p->mYExtent, 1.0f);
	//			}
	//			break;
	//		}

	//		if(tlm)
	//		{
	//			BasicGeometry* bg = NZ_New BasicGeometry(name, static_cast<RenderMesh*>(tlm));
	//			return static_cast<SceneNode*>(bg);
	//		}

	//		return NULL;
	//	}
	//};

	SceneGraph::SceneGraph()
		:mRootNode(NULL)
	{
		//mBulitInMeshCreator = NZ_New BuiltInMeshCreator();
		mRenderer = Renderer::GetGlobal();
	}

	SceneGraph::~SceneGraph()
	{
		//if(mBulitInMeshCreator)
		//{
		//	NZ_Delete mBulitInMeshCreator;
		//}

		if(mRootNode)
		{
			NZ_Delete mRootNode;
		}
	}

	void SceneGraph::initialize()
	{
		_initialize();

		mRootNode = createNodeImpl("NezhaSceneRoot");
	}

	SceneNode* SceneGraph::createSceneNode(const _string& name, SceneNode* parent)
	{
		if(findSceneNode(name))
		{
			NZError("Scene node %s already exsit.");

			return NULL;
		}

		SceneNode* sn = createNodeImpl(name);

		if(parent)
		{
			parent->attachChild(sn);
		}
		else
		{
			mRootNode->attachChild(sn);
		}

		return sn;
	}

	Camera* SceneGraph::createCamera(const _string& name, bool perspective)
	{
		if(getCamera(name))
		{
			NZError("Object %s already exsit.", name.c_str());

			return NULL;
		}

		Camera* cam = NZ_New Camera(name, perspective);
		mCameras.insert(MapPair<_string, Camera*>(name, cam));

		return cam;
	}

	Camera* SceneGraph::getCamera(const _string& name)
	{
		CameraNameMap::iterator it = mCameras.find(name);

		if(it != mCameras.end())
		{
			return it->second;
		}

		return NULL;
	}

	void SceneGraph::destroyCamera(Camera* cam)
	{
		if(cam)
		{
			CameraNameMap::iterator it = mCameras.find(cam->getName());

			if(it != mCameras.end())
			{
				// 
				Renderer::GetGlobal()->notifyCameraDestroy(cam);

				NZ_Delete it->second;
				mCameras.erase(it);
			}	
		}
	}

	Light* SceneGraph::createLight(const _string& name)
	{
		LightNameMap::iterator it = mLights.find(name);

		if(it != mLights.end())
		{
			NZError("Light %s already exsit.");

			return NULL;
		}

		Light* l = NZ_New Light(name);

		mLights.insert(MapPair<_string, Light*>(name, l));

		return l;
	}

	Light* SceneGraph::getLight(const _string& name)
	{
		LightNameMap::iterator it = mLights.find(name);

		return it != mLights.end() ? it->second : NULL;
	}

	void SceneGraph::destroyLight(Light* lit)
	{
		if(lit)
		{
			LightNameMap::iterator it = mLights.find(lit->getName());

			if(it != mLights.end())
			{
				NZ_Delete lit;
				mLights.erase(it);
			}
		}
	}

	void SceneGraph::getAffectLights(LightList& lights, SceneNode* sn)
	{
		if(!sn)
		{
			lights.clear();
			return;
		}

		Vector3 center;
		float radius = 0;
		lights.clear();

		for(LightNameMap::iterator it = mLights.begin(); it != mLights.end(); it++)
		{
			if(!it->second->mEnabled)
			{
				continue;
			}

			if(it->second->getLitType() == LT_DIRECTIONAL)
			{
				lights.addLight(it->second);
			}
			else
			{
				it->second->getBoundingSpere(center, radius);
				const AABB& aabb = sn->getWorldAABB(0);

				if(aabb.intersectSphere(center, radius))
				{
					lights.addLight(it->second);
				}
			}

			if(lights.getNumLights() >= LightList::MAX_LENGTH)
			{
				return;
			}
		}
	}

	SceneNode* SceneGraph::create(const _string& name, SceneObjectCreation* sc)
	{
		if(!sc)
		{
			return NULL;
		}

		if(findSceneNode(name))
		{
			NZError("Object %s already exsit, when try to create it from script.", name.c_str());

			return NULL;
		}

		SceneNode* so = sc->create(name);
		if(so)
		{
			_insertNode(so);
		}

		return NULL;
	}

	BasicGeometry* SceneGraph::createBuiltInBox(const _string& name)
	{
		if(findSceneNode(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		TriangleListMesh* tlm = TriangleListMesh::CreateBox(1.0f, 1.0f, 1.0f);
		BasicGeometry* bg = NZ_New BasicGeometry(name, static_cast<RenderMesh*>(tlm));

		_insertNode(static_cast<SceneNode*>(bg));

		return bg;
	}

	BasicGeometry* SceneGraph::createBuiltInPlane(const _string& name, u16 xExtent, u16 yExtent)
	{
		if(findSceneNode(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		TriangleListMesh* tlm = TriangleListMesh::CreatePlane(xExtent, yExtent);
		BasicGeometry* bg = NZ_New BasicGeometry(name, static_cast<RenderMesh*>(tlm));

		_insertNode(static_cast<SceneNode*>(bg));

		return bg;
	}

	BasicGeometry* SceneGraph::createBuiltInSphere(const _string& name)
	{
		if(findSceneNode(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		TriangleListMesh* tlm = TriangleListMesh::CreateSphere(5, 10, 1.0f);
		BasicGeometry* bg = NZ_New BasicGeometry(name, static_cast<RenderMesh*>(tlm));

		_insertNode(static_cast<SceneNode*>(bg));

		return bg;
	}

	void SceneGraph::determineVisibleObjects(RenderTarget* rt, int flag)
	{
		Camera* cam = rt->getCamera();

		// TODO cull by mask

		cullObjectsByCamera(rt, mRootNode, flag);
	}

	void SceneGraph::determinePSC(Light* lit)
	{
	}

	void SceneGraph::cullLightsByCamera(Camera* cam, LightVector& lights)
	{
		lights.clear();

		for(LightNameMap::iterator it = mLights.begin(); it != mLights.end(); it++)
		{
			if(it->second->mEnabled && !cullLight(cam, it->second))
			{
				lights.addLight(it->second);
			}
		}
	}

	void SceneGraph::destroyAllObjects()
	{
		_destroyAllObjects();
		destroyAllLights();
		destroyAllCameras();
	}

	void SceneGraph::destroyAllCameras()
	{
		for(CameraNameMap::iterator it = mCameras.begin(); it != mCameras.end(); it++)
		{
			NZ_Delete it->second;
		}

		mCameras.clear();
	}

	void SceneGraph::destroyAllLights()
	{
		for(LightNameMap::iterator it = mLights.begin(); it != mLights.end(); it++)
		{
			NZ_Delete it->second;
		}

		mLights.clear();
	}

	void SceneGraph::cullObjectsByCamera(RenderTarget* rt, SceneNode* sn, int flag)
	{
		u32 numChildren = sn->getNumChildren();
		for(u32 i = 0;i < numChildren; i++)
		{
			SceneNode* sub = sn->getChild(i);
			
			if(!(rt->getCamera()->getCullingMask() & sn->getCullingMask())
				|| !(rt->getVisiblityMask() & sn->getCullingMask()))
			{
				continue;
			}

			if(sub->getCustomCulling())
			{
				if(!sub->getCustomCulling()->isSceneNodeVisible(sn, rt))
				{
					continue;
				}
			}

			// TODO frustum culling
			
			// not culled by camera
			sub->addToRenderQueue();

			cullObjectsByCamera(rt, sub, flag);
		}
	}

	bool SceneGraph::cullLight(Camera* cam, Light* lit)
	{
		FrustumPlanes fp;

		Vector3 center;
		float radius = 0;
		lit->getBoundingSpere(center, radius);

		FrustumPlane p = cam->cull(center, radius, fp);

		return p != FP_NONE;
	}

}//end namespace Nezha